package tddc17;

import java.util.LinkedList;

import aima.core.environment.liuvacuum.*;
import aima.core.agent.Action;
import aima.core.agent.AgentProgram;
import aima.core.agent.Percept;
import aima.core.agent.impl.*;

class MyAgentState {
	public int[][] world = new int[22][22];
	public int initialized = 0;
	final int UNKNOWN = 0;
	final int WALL = 1;
	final int CLEAR = 2;
	final int DIRT = 3;
	final int ACTION_NONE = 0;
	final int ACTION_MOVE_FORWARD = 1;
	final int ACTION_TURN_RIGHT = 2;
	final int ACTION_TURN_LEFT = 3;
	final int ACTION_SUCK = 4;
	public int agent_x_position = 1;
	public int agent_y_position = 1;
	public int agent_last_action = ACTION_NONE;

	MyAgentState() {
		for (int i = 0; i < world.length; i++)
			for (int j = 0; j < world[i].length; j++)
				world[i][j] = UNKNOWN;
		world[1][1] = CLEAR;
		agent_last_action = ACTION_NONE;
	}

	public void updateWorld(int x_position, int y_position, int info) {
		world[x_position][y_position] = info;
	}

	public void printWorldDebug() {
		for (int i = 0; i < world.length; i++) {
			for (int j = 0; j < world[i].length; j++) {
				if (world[j][i] == UNKNOWN)
					System.out.print(" ? ");
				if (world[j][i] == WALL)
					System.out.print(" # ");
				if (world[j][i] == CLEAR)
					System.out.print(" . ");
				if (world[j][i] == DIRT)
					System.out.print(" D ");
			}
			System.out.println("");
		}
	}
}

class MyAgentProgram implements AgentProgram {
	// Here you can define your variables!
	public int iterationCounter = 510;
	public MyAgentState state = new MyAgentState();
	public char direction = 'E';
	public boolean finished = false;
	public LinkedList<Action> stack = new LinkedList<Action>();

	@Override
	public Action execute(Percept percept) {
		// This example agent program will update the internal agent state while
		// only moving forward.
		// Note! It works under the assumption that the agent starts facing to
		// the right.
		iterationCounter--;
		if (iterationCounter == 0)
			return NoOpAction.NO_OP;
		DynamicPercept p = (DynamicPercept) percept;
		Boolean bump = (Boolean) p.getAttribute("bump");
		Boolean dirt = (Boolean) p.getAttribute("dirt");
		Boolean home = (Boolean) p.getAttribute("home");
		System.out.println("percept: " + p);

		if (dirt)
			state.updateWorld(state.agent_x_position, state.agent_y_position,
					state.DIRT);
		else
			state.updateWorld(state.agent_x_position, state.agent_y_position,
					state.CLEAR);

		// State update based on the percept value and the last action
		// This block of code adjusts the cleaner's location on the map as it moves forward.
		if (state.agent_last_action == state.ACTION_MOVE_FORWARD) {
			if (direction == 'E') {
				if (!bump) {
					state.agent_x_position++;
				} else {
					state.updateWorld(state.agent_x_position + 1,
							state.agent_y_position, state.WALL);
				}
			} else if (direction == 'S') {
				if (!bump) {
					state.agent_y_position++;
				} else {
					state.updateWorld(state.agent_x_position,
							state.agent_y_position + 1, state.WALL);
				}
			} else if (direction == 'W') {
				if (!bump) {
					state.agent_x_position--;
				} else {
					state.updateWorld(state.agent_x_position - 1,
							state.agent_y_position, state.WALL);
				}
			} else if (direction == 'N') {
				if (!bump) {
					state.agent_y_position--;
				} else {
					state.updateWorld(state.agent_x_position,
							state.agent_y_position - 1, state.WALL);
				}
			}
		}
		// Next action selection based on the percept value
		if (home && finished) {
			System.out.println("The map is clean!");
			return NoOpAction.NO_OP;
		}
		// This block is only used after the map is cleaned.
		// The cleaner now moves towards the upper left corner.
		if (finished) {
			System.out.println("Going home now.");
			if (direction == 'N'
					&& state.world[state.agent_x_position][state.agent_y_position - 1] == state.WALL) {
				direction = 'W';
				state.agent_last_action = state.ACTION_TURN_LEFT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_LEFT);
				return stack.poll();
			} else if (direction == 'E'
					&& state.world[state.agent_x_position][state.agent_y_position - 1] == state.WALL) {
				direction = 'N';
				state.agent_last_action = state.ACTION_TURN_LEFT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_LEFT);
				return stack.poll();
			} else if (direction == 'W'
					&& state.world[state.agent_x_position - 1][state.agent_y_position] == state.WALL) {
				direction = 'N';
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				return stack.poll();
			} else if (direction == 'N') {
				state.agent_last_action = state.ACTION_MOVE_FORWARD;
				stack.add(LIUVacuumEnvironment.ACTION_MOVE_FORWARD);
				return stack.poll();
			} else if (direction == 'W') {
				state.agent_last_action = state.ACTION_MOVE_FORWARD;
				stack.add(LIUVacuumEnvironment.ACTION_MOVE_FORWARD);
				return stack.poll();
			}
		}

		if (!checkMap()) {
			finished = true;
		}
		// This block of code instructs the cleaner to turn when it hits a spot on the map
		//where there is a wall, a cleaned spot, or a previously dirty spot.
		if (dirt) {
			System.out.println("DIRT -> choosing SUCK action!");
			state.agent_last_action = state.ACTION_SUCK;
			stack.add(LIUVacuumEnvironment.ACTION_SUCK);
			return stack.poll();
		} else {
			if (bump && direction == 'E') {
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				//state.agent_last_action = state.ACTION_TURN_RIGHT;
				direction = 'S';
				System.out.println("direction: " + direction);
				state.printWorldDebug();
				return stack.poll();
			} else if (bump && direction == 'S') {
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				//state.agent_last_action = state.ACTION_TURN_RIGHT;
				direction = 'W';
				System.out.println("direction: " + direction);
				state.printWorldDebug();
				return stack.poll();
			} else if (bump && direction == 'W') {
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				//state.agent_last_action = state.ACTION_TURN_RIGHT;
				direction = 'N';
				System.out.println("direction: " + direction);
				state.printWorldDebug();
				return stack.poll();
			} else if (bump && direction == 'N') {
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				//state.agent_last_action = state.ACTION_TURN_RIGHT;
				direction = 'E';
				System.out.println("direction: " + direction);
				state.printWorldDebug();
				return stack.poll();
			} else if (bump && direction == 'S') {
				//state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_LEFT);
				state.agent_last_action = state.ACTION_TURN_LEFT;
				direction = 'E';
				System.out.println("direction: " + direction);
				state.printWorldDebug();
				return stack.poll();
			} else if (direction == 'E'
					&& ((state.world[state.agent_x_position + 1][state.agent_y_position] == state.CLEAR) || (state.world[state.agent_x_position + 1][state.agent_y_position] == state.DIRT))) {
				direction = 'S';
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				return LIUVacuumEnvironment.ACTION_TURN_RIGHT;
			} else if (direction == 'S'
					&& ((state.world[state.agent_x_position][state.agent_y_position + 1] == state.CLEAR) || (state.world[state.agent_x_position][state.agent_y_position + 1] == state.DIRT))) {
				direction = 'W';
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				return stack.poll();
			} else if (direction == 'N'
					&& ((state.world[state.agent_x_position][state.agent_y_position - 1] == state.CLEAR) || (state.world[state.agent_x_position][state.agent_y_position - 1] == state.DIRT))) {
				direction = 'E';
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				return stack.poll();
			} else if (direction == 'W'
					&& ((state.world[state.agent_x_position - 1][state.agent_y_position] == state.CLEAR) || (state.world[state.agent_x_position - 1][state.agent_y_position] == state.DIRT))) {
				direction = 'N';
				state.agent_last_action = state.ACTION_TURN_RIGHT;
				stack.add(LIUVacuumEnvironment.ACTION_TURN_RIGHT);
				return stack.poll();
			} else {
				state.agent_last_action = state.ACTION_MOVE_FORWARD;
				stack.add(LIUVacuumEnvironment.ACTION_MOVE_FORWARD);
				System.out.println("direction: " + direction);
				state.printWorldDebug();
				return stack.poll();
			}
		}
	}
	
	// This method determines if there are any unknown states left surrounding the cleaner.
	public boolean checkMap() {
		boolean unfinished = false;
		if (state.world[state.agent_x_position + 1][state.agent_y_position] == state.UNKNOWN) {
			unfinished = true;
		} else if (state.world[state.agent_x_position][state.agent_y_position + 1] == state.UNKNOWN) {
			unfinished = true;
		} else if (state.world[state.agent_x_position - 1][state.agent_y_position] == state.UNKNOWN) {
			unfinished = true;
		} else if (state.world[state.agent_x_position][state.agent_y_position - 1] == state.UNKNOWN) {
			unfinished = true;
		}
		return unfinished;
	}
}

public class MyVacuumAgent extends AbstractAgent {
	public MyVacuumAgent() {
		super(new MyAgentProgram());
	}
}